package com.guozcc.gateway.autofilter.autogatewayfilter;

import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.ResourceTypeConstants;
import com.alibaba.csp.sentinel.adapter.reactor.ContextConfig;
import com.alibaba.csp.sentinel.adapter.reactor.EntryConfig;
import com.alibaba.csp.sentinel.adapter.reactor.SentinelReactorTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.ArrayList;


/***
 * Sentinel流控过滤器
 */
@Component
public class SentinelGatewayFilter implements GatewayFilter , Ordered  {
    private final Logger log = LoggerFactory.getLogger(this.getClass());


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//        //获取请求报文
//        JSONObject requestJson = null;
//        try {
//            String msgRequest = (String)exchange.getAttributes().get("msg_request");
//            if(msgRequest!=null){
//                requestJson = JSONObject.parseObject(msgRequest);
//            }
//        } catch (Exception e){
//            requestJson = new JSONObject();
//            log.error("解析请求报文异常",e);
//        }
//        log.info("获取请求报文："+requestJson);

        URI uri = exchange.getRequest().getURI();
        String uriPath = uri.getPath();
        String uriHost = uri.getHost();
//        String host1 = exchange.getRequest().getRemoteAddress().getHostString();
//        String host2 = exchange.getRequest().getURI().getHost();
//        String host3 = exchange.getRequest().getHeaders().getFirst("Host");
//        String hostAddress = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();



        //响应式编程，要在流管道上做流控
        return chain.filter(exchange)
                .transform(
                        new SentinelReactorTransformer(
                                new EntryConfig(
                                        //资源名称
                                        uriPath,
                                        //resourceType
                                        ResourceTypeConstants.COMMON_API_GATEWAY,
                                        //准入or准出
                                        EntryType.IN,
                                        /*
                                         * acquirecount 指本次申请通过的数量，一般来说为1
                                         *
                                         * 这里说明一下：
                                         *      curcount 是根据阈值类型拿出来的当前阈值,
                                         *      count 是规则里面配置的限流阈值。
                                         *      通过 curcount + acquirecount > count 来判断 是否超过阈值了。
                                         *      这个地方有一个问题，就是判断公式是一个非原子性的，
                                         *      curcount是从Node里面取出来， 但是对Node进行统计的是在Statisticslot 进行统计的，
                                         *      在高并发场景下，如果有多个线程执行到了这个位置 （curcount +acquireCount>count）， 得到的结果就不是绝对正确的，所以sentinel的限流，不是绝对准确的，会存在一定误差。
                                         *      这种缺陷是基于他的责任链设计，每个sot负责各自的功能，一个统计，一个判断，两者没有在一起，这就必然导致了会有并发问题。
                                         *      解决方法就是简单粗暴的加个锁上去，但是sentinel官方好像并没有打算修复这个问题。
                                         *      锁太耗费性能了，我认为限流一般是在高并发场景下使用的，而这种高并发下的误差问题是无伤大雅的，Sentinel只是基于性能和准确性的综合考量后选择了前者。
                                         */
                                        1,
                                        //热点参数数组
                                        new ArrayList<String>() {{
                                            add(uriHost);
                                            add("terminaltype");
                                        }}.toArray(),
                                        //指定来源，授权流控会用到，ContextUtil.enter("API", "limitApp");
                                        new ContextConfig("API", "limitApp")
                                )
                        )
                );

        //                    系统自动统计了，不需要自己统计了
//                .onErrorMap(throwable -> {
//            if (throwable instanceof BlockException) {
//                log.info("Sentinel拦截了");
//                Tracer.trace((Throwable) throwable);//记录异常数与异常比例数，s熔断降级使用
//                return throwable;
//            } else {
//                log.error("非流控异常",throwable);
//                Tracer.trace((Throwable) throwable);//记录异常数与异常比例数，s熔断降级使用
//                return throwable;
//            }
//        })


//                .doOnError(s -> {
//                    if(s instanceof BlockException){//BlockException 流控拦截异常
//                        log.info("Sentinel拦截了");
//                    }else if(s instanceof Throwable){//非BlockException 系统异常，统计错误数
//                        Throwable err = (Throwable) s;
//                        log.error("非流控异常",err);
//                        Tracer.trace((Throwable) s);//记录异常数与异常比例数，s熔断降级使用
//                    }else {//怎么会进来这里呢？直接中断
//                        throw new RuntimeException();
//                    }
//                })

        //Sentinel流控
//        ResultVO fail = ResultUtils.fail(GatewayStatus.DEFAULT_EXCEPTION.errcode(), GatewayStatus.DEFAULT_EXCEPTION.errmsg());
//        log.info("sentinel start " + uri);
//        Entry entry = null;
//        try {
//            ContextUtil.enter("API", "limitApp");//授权流控使用
//            entry = SphU.entry(uriPath, EntryType.IN, 1, uriHost,"terminaltype");
//            log.info("sentinel pass " + uri);
//            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
//                log.info("SentinelGatewayFilter后置逻辑");
//            }));
//        }catch (BlockException e) {
//            log.info("sentinel block " + uri);
//            fail = ResultUtils.fail(GatewayStatus.SENTINEL_EXCEPTION.errcode(), GatewayStatus.SENTINEL_EXCEPTION.errmsg());
//        }catch (Exception oe){
//            log.error("",oe);
//            if (!BlockException.isBlockException(oe)) {
//                Tracer.trace(oe);//记录异常数与异常比例数，熔断降级使用
//            }
//            fail = ResultUtils.fail(GatewayStatus.DEFAULT_EXCEPTION.errcode(), oe.getLocalizedMessage());
//        }finally {
//            if (entry != null){
//                entry.exit(1, uriHost,"terminaltype");
//            }
//            ContextUtil.exit();
//        }
//        log.info("sentinel end " + uri);
//        // 异常，组异常报文返回
//        response.setStatusCode(HttpStatus.OK);
//        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
//        return response.writeWith(Mono.just(response.bufferFactory().wrap(JSON.toJSONString(fail).getBytes())));

    }

    @Override
    public int getOrder() {
        return 1;
    }
}
